﻿using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
using System.Collections.Specialized;

namespace JQueryControlToolkit
{
    public class JQuery : DomResult
    {
        static StringDictionary OperationsMapping;

        public static string GetPlugInMethod(string operation)
        {
            return OperationsMapping[operation];
        }

        static JQuery()
        {
            OperationsMapping = new StringDictionary();
            OperationsMapping.Add("JQuery:Each", "each");
            OperationsMapping.Add("JQuery:Eq", "eq");
            OperationsMapping.Add("JQuery:Queue", "queue");
            OperationsMapping.Add("JQuery:Dequeue", "dequeue");
            OperationsMapping.Add("JQuery:Ready", "ready");
            OperationsMapping.Add("JQuery:Bind", "bind");
            OperationsMapping.Add("JQuery:One", "one");
            OperationsMapping.Add("JQuery:Trigger", "trigger");
            OperationsMapping.Add("JQuery:TriggerHandler", "triggerHandler");
            OperationsMapping.Add("JQuery:Unbind", "unbind");
            OperationsMapping.Add("JQuery:Live", "live");
            OperationsMapping.Add("JQuery:Die", "die");
            OperationsMapping.Add("JQuery:Hover", "hover(@over, @out)");
            OperationsMapping.Add("JQuery:Toggle", "toggle");
            OperationsMapping.Add("JQuery:Blur", "Blur");
            OperationsMapping.Add("JQuery:Change", "change");
            OperationsMapping.Add("JQuery:Click", "click");
            OperationsMapping.Add("JQuery:DblClick", "dblClick");
            OperationsMapping.Add("JQuery:Error", "error");
            OperationsMapping.Add("JQuery:Focus", "focus");
            OperationsMapping.Add("JQuery:KeyDown", "keydown");
            OperationsMapping.Add("JQuery:KeyPress", "keypress");
            OperationsMapping.Add("JQuery:KeyUp", "keyup");
            OperationsMapping.Add("JQuery:MouseDown", "mousedown");
            OperationsMapping.Add("JQuery:MouseMove", "mousemove");
            OperationsMapping.Add("JQuery:MouseOut", "mouseout");
            OperationsMapping.Add("JQuery:MouseOver", "mouseover");
            OperationsMapping.Add("JQuery:MouseUp", "mouseup");
            OperationsMapping.Add("JQuery:Resize", "resize");
            OperationsMapping.Add("JQuery:Scroll", "scroll");
            OperationsMapping.Add("JQuery:Select", "select");
            OperationsMapping.Add("JQuery:Submit", "submit");
            OperationsMapping.Add("JQuery:Unload", "unload");
            OperationsMapping.Add("JQuery:Animate", "animate");
            OperationsMapping.Add("JQuery:FadeIn", "fadeIn");
            OperationsMapping.Add("JQuery:FadeOut", "fadeOut");
            OperationsMapping.Add("JQuery:FadeTo", "fadeTo");
            OperationsMapping.Add("JQuery:Hide", "hide");
            OperationsMapping.Add("JQuery:Show", "show");
            OperationsMapping.Add("JQuery:SlideDown", "slideDown");
            OperationsMapping.Add("JQuery:SlideToggle", "slideToggle");
            OperationsMapping.Add("JQuery:SlideUp", "slideUp");
            OperationsMapping.Add("JQuery:Stop", "stop");
            OperationsMapping.Add("JQuery:Attr", "attr");
            OperationsMapping.Add("JQuery:RemoveAttr", "removeAttr");
            OperationsMapping.Add("JQuery:AddClass", "addClass");
            OperationsMapping.Add("JQuery:RemoveClass", "removeClass");
            OperationsMapping.Add("JQuery:ToggleClass", "toggleClass");
            OperationsMapping.Add("JQuery:Html", "html");
            OperationsMapping.Add("JQuery:Text", "text");
            OperationsMapping.Add("JQuery:Val", "val");
            OperationsMapping.Add("JQuery:Filter", "filter");
            OperationsMapping.Add("JQuery:Map", "map");
            OperationsMapping.Add("JQuery:Not", "not");
            OperationsMapping.Add("JQuery:Slice", "slice");
            OperationsMapping.Add("JQuery:Add", "add");
            OperationsMapping.Add("JQuery:Children", "children");
            OperationsMapping.Add("JQuery:Contents", "contents");
            OperationsMapping.Add("JQuery:Find", "find");
            OperationsMapping.Add("JQuery:Next", "next");
            OperationsMapping.Add("JQuery:NextAll", "nextAll");
            OperationsMapping.Add("JQuery:Parent", "parent");
            OperationsMapping.Add("JQuery:Parents", "parents");
            OperationsMapping.Add("JQuery:Prev", "prev");
            OperationsMapping.Add("JQuery:PrevAll", "prevAll");
            OperationsMapping.Add("JQuery:Siblings", "siblings");
            OperationsMapping.Add("JQuery:AndSelf", "andSelf");
            OperationsMapping.Add("JQuery:End", "end");

            OperationsMapping.Add("JQuery:Append", "append");
            OperationsMapping.Add("JQuery:AppendTo", "appendTo");
            OperationsMapping.Add("JQuery:Prepend", "prepend");
            OperationsMapping.Add("JQuery:PrependTo", "prependTo");
            OperationsMapping.Add("JQuery:After", "after");
            OperationsMapping.Add("JQuery:Before", "before");
            OperationsMapping.Add("JQuery:InsertAfter", "insertAfter");
            OperationsMapping.Add("JQuery:InsertBefore", "insertBefore");
            OperationsMapping.Add("JQuery:Wrap", "wrap");
            OperationsMapping.Add("JQuery:WrapAll", "wrapAll");
            OperationsMapping.Add("JQuery:WrapInner", "wrapInner");
            OperationsMapping.Add("JQuery:ReplaceAll", "replaceAll");
            OperationsMapping.Add("JQuery:ReplaceWith", "replaceWith");
            OperationsMapping.Add("JQuery:Empty", "empty");
            OperationsMapping.Add("JQuery:Remove", "remove");
            OperationsMapping.Add("JQuery:Clone", "clone");

            OperationsMapping.Add("JQuery:Css", "css");
            OperationsMapping.Add("JQuery:Height", "height");
            OperationsMapping.Add("JQuery:Width", "width");

            OperationsMapping.Add("JQuery:Load", "load");

            OperationsMapping.Add("JQuery:DisableSelection", "disableSelection");
            OperationsMapping.Add("JQuery:EnableSelection", "enableSelection");
        }

        private JSObject _selector;
        JavaScriptSerializer serializer = new JavaScriptSerializer();

        public static DomResult Call(string pluginName, params object[] parameters)
        {
            var domResult = new DomResult();
            domResult.AddOperation(pluginName, parameters, "$." + pluginName);
            return domResult;
        }

        public JQuery(string selector) : this(new JSString(selector), false) { }
        public JQuery(JSObject selector) : this(selector, false) { }

        internal JQuery(JSObject selector, bool isParameter)
        {
            _selector = selector;
            if (_selector != null)
            {
                if (isParameter)
                {
                    AddParameter(selector.ToString());
                    AddOperation("JQuery:$With", selector, "$(" + selector + ")");
                }
                else
                {
                    AddOperation("JQuery:$", selector, "$");
                }
            }
            else
            {

            }
        }

        public JSObject Selector
        {
            get
            {
                return _selector;
            }
        }

        #region Core
        // See http://docs.jquery.com/Core

        public JQuery Each(string callback)
        {
            return CallPlugIn("JQuery:Each", "each", callback);
        }

        public JQuery Each(DomResult jquery)
        {
            return CallPlugIn("JQuery:Each", "each", jquery);
        }

        public JQuery Eq(int index)
        {
            return CallPlugIn("JQuery:Eq", "eq", index);
        }

        //TODO: Queue(string name), Queue(string name, string jquery)

        public JQuery Queue(string callback)
        {
            return CallPlugIn("JQuery:Queue", "queue", callback);
        }

        public JQuery Queue(JQuery jquery)
        {
            return CallPlugIn("JQuery:Queue", "queue", jquery);
        }

        //TODO: Dequeue(string name)

        public JQuery Dequeue()
        {
            return CallPlugIn("JQuery:Dequeue", "dequeue", (object)null);
        }

        #endregion

        #region Events

        public JQuery Ready(string callback)
        {
            return CallPlugIn("JQuery:Ready", "ready", callback);
        }

        public JQuery Ready(JQuery jquery)
        {
            return CallPlugIn("JQuery:Ready", "ready", jquery);
        }

        public JQuery Bind(string type, string callback)
        {
            return CallPlugIn("JQuery:Bind", "bind", type, callback);
        }

        public JQuery Bind(string type, JQuery jquery)
        {
            return CallPlugIn("JQuery:Bind", "bind", type, jquery);
        }

        public JQuery Bind(string type, Dictionary<string, object> data, string callback)
        {
            return Bind(type, data, callback);
        }

        public JQuery Bind(string type, object data, string callback)
        {
            return CallPlugIn("JQuery:Bind", "bind", type, new JavaScriptSerializer().Serialize(data), callback);
        }

        public JQuery Bind(string type, object data, JQuery jquery)
        {
            return CallPlugIn("JQuery:Bind", "bind", type, new JavaScriptSerializer().Serialize(data), jquery);
        }

        public JQuery One(string type, string callback)
        {
            return CallPlugIn("JQuery:One", "one", type, callback);
        }

        public JQuery One(string type, JQuery jquery)
        {
            return One(type, jquery.ToScript());
        }

        public JQuery One(string type, Dictionary<string, object> data, string callback)
        {
            return One(type, data, callback);
        }

        public JQuery One(string type, object data, string callback)
        {
            return CallPlugIn("JQuery:One", "one",
                new Dictionary<string, object>() { { "Type", type }, { "Data", serializer.Serialize(data) }, { "Callback", callback } });
        }

        public JQuery One(string type, object data, JQuery jquery)
        {
            return One(type, data, jquery.ToScript());
        }

        public JQuery Trigger(string type)
        {
            return CallPlugIn("JQuery:Trigger", "trigger", type);
        }

        public JQuery Trigger(string type, Dictionary<string, object> data)
        {
            return Trigger(type, data);
        }

        public JQuery Trigger(string type, object data)
        {
            return CallPlugIn("JQuery:Trigger", "trigger",
                new Dictionary<string, object>() { { "Type", type }, { "Data", new JavaScriptSerializer().Serialize(data) } });
        }

        public JQuery TriggerHandler(string type)
        {
            return CallPlugIn("JQuery:TriggerHandler", "triggerHandler", type);
        }

        public JQuery TriggerHandler(string type, Dictionary<string, object> data)
        {
            return TriggerHandler(type, data);
        }

        public JQuery TriggerHandler(string type, object data)
        {
            return CallPlugIn("JQuery:TriggerHandler", "triggerHandler",
                new Dictionary<string, object>() { { "Type", type }, { "Data", new JavaScriptSerializer().Serialize(data) } });
        }

        public JQuery Unbind()
        {
            return CallPlugIn("JQuery:Unbind", "unbind");
        }

        public JQuery Unbind(string type)
        {
            return CallPlugIn("JQuery:Unbind", "unbind", type);
        }

        public JQuery Unbind(string type, string data)
        {
            return CallPlugIn("JQuery:Unbind", "unbind", type, data);
        }

        public JQuery Live(string type, JQuery jquery)
        {
            return Live(type, Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Live(string type, JSFunction jsFunction)
        {
            return CallPlugIn("JQuery:Live", "live", type, jsFunction);
        }

        public JQuery Live(string type, string callback)
        {
            return CallPlugIn("JQuery:Live", "live",
                new Dictionary<string, object>() { { "Type", type }, { "Callback", callback } });
        }

        public JQuery Die()
        {
            return CallPlugIn("JQuery:Die", "die");
        }

        public JQuery Die(string type)
        {
            return CallPlugIn("JQuery:Die", "die", type);
        }

        public JQuery Die(string type, JQuery jquery)
        {
            return Die(type, Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Die(string type, JSFunction callback)
        {
            return CallPlugIn("JQuery:Die", "die",
                new Dictionary<string, object>() { { "Type", type }, { "Callback", callback } });
        }


        public JQuery Hover(string overHandler, string outHandler)
        {
            AddOperation("JQuery:TriggerHandler", null, ".hover(" + overHandler + "," + outHandler + ")");
            return this;
        }

        public JQuery Toggle(ToggleEffect effect)
        {
            return CallPlugIn("JQuery:Toggle", "toggle", effect.ToString().ToLower());
        }

        public JQuery Toggle(params string[] handlers)
        {
            if (handlers.Length < 2)
            {
                throw new ArgumentException("Too few handlers. There must be at least two.", "handlers");
            }
            return CallPlugIn("JQuery:Toggle", "triggetHandler", string.Join(",", handlers));
        }

        public JQuery Blur()
        {
            return CallPlugIn("JQuery:Blur", "blur");
        }

        public JQuery Blur(string callback)
        {
            return CallPlugIn("JQuery:Blur", "blur", callback);
        }

        public JQuery Blur(JQuery jquery)
        {
            return Blur(jquery.ToScript());
        }

        public JQuery Change()
        {
            return CallPlugIn("JQuery:Change", "change");
        }

        public JQuery Change(JSFunction callback)
        {
            return CallPlugIn("JQuery:Change", "change", callback);
        }

        public JQuery Change(JQuery jquery)
        {
            return Change(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Click()
        {
            return CallPlugIn("JQuery:Click", "click");
        }

        public JQuery Click(string callback)
        {
            return CallPlugIn("JQuery:Click", "click", callback);
        }

        public JQuery Click(JQuery callback)
        {
            return CallPlugIn("JQuery:Click", "click", Extensions.ToFunctionExpression(callback));
        }

        public JQuery DblClick()
        {
            return CallPlugIn("JQuery:DblClick", "dblclick");
        }

        public JQuery DblClick(string callback)
        {
            return CallPlugIn("JQuery:DblClick", "dblclick", callback);
        }

        public JQuery DblClick(JQuery jquery)
        {
            return CallPlugIn("JQuery:DblClick", "dblclick", jquery);
        }

        public JQuery Error()
        {
            return CallPlugIn("JQuery:Error", "error");
        }

        public JQuery Error(string callback)
        {
            return CallPlugIn("JQuery:Error", "error", callback);
        }

        public JQuery Error(JQuery jquery)
        {
            return CallPlugIn("JQuery:Error", "error", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Focus()
        {
            return CallPlugIn("JQuery:Focus", "focus");
        }

        public JQuery Focus(string callback)
        {
            return CallPlugIn("JQuery:Focus", "focus", callback);
        }

        public JQuery Focus(JQuery jquery)
        {
            return CallPlugIn("JQuery:Focus", "focus", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery KeyDown()
        {
            return CallPlugIn("JQuery:KeyDown", "keydown");
        }

        public JQuery KeyDown(string callback)
        {
            return CallPlugIn("JQuery:KeyDown", "keydown", callback);
        }

        public JQuery KeyDown(JQuery jquery)
        {
            return CallPlugIn("JQuery:KeyDown", "keydown", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery KeyPress()
        {
            return CallPlugIn("JQuery:KeyPress", "keypress");
        }

        public JQuery KeyPress(string callback)
        {
            return CallPlugIn("JQuery:KeyPress", "keypress", callback);
        }

        public JQuery KeyPress(JQuery jquery)
        {
            return CallPlugIn("JQuery:KeyPress", "keypress", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery KeyUp()
        {
            return KeyUp((string)null);
        }

        public JQuery KeyUp(string callback)
        {
            return CallPlugIn("JQuery:KeyUp", "keyup", callback);
        }

        public JQuery KeyUp(JQuery jquery)
        {
            return CallPlugIn("JQuery:KeyUp", "keyup", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery LoadEvent(string callback)
        {
            return CallPlugIn("JQuery:LoadEvent", "load", callback);
        }

        public JQuery MouseDown()
        {
            return CallPlugIn("JQuery:MouseDown", "mousedown");
        }

        public JQuery MouseMove(string callback)
        {
            return CallPlugIn("JQuery:MouseMove", "mousemove", callback);
        }

        public JQuery MouseMove(JQuery jquery)
        {
            return CallPlugIn("JQuery:MouseMove", "mousemove", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery MouseOut(JSFunction callback)
        {
            return CallPlugIn("JQuery:MouseOut", "mouseout", callback);
        }

        public JQuery MouseOut(JQuery jquery)
        {
            return MouseOut(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery MouseOver(JSFunction callback)
        {
            return CallPlugIn("JQuery:MouseOver", "mouseover", callback);
        }

        public JQuery MouseOver(JQuery jquery)
        {
            return MouseOver(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery MouseUp(JSFunction callback)
        {
            return CallPlugIn("JQuery:MouseUp", "mouseup", callback);
        }

        public JQuery MouseUp(JQuery jquery)
        {
            return CallPlugIn("JQuery:MouseUp", "mouseup", Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Resize(JSFunction callback)
        {
            return CallPlugIn("JQuery:Resize", "resize", callback);
        }

        public JQuery Resize(JQuery jquery)
        {
            return Resize(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Scroll(JSFunction callback)
        {
            return CallPlugIn("JQuery:Scroll", "scroll", callback);
        }

        public JQuery Scroll(JQuery jquery)
        {
            return Scroll(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Select()
        {
            return CallPlugIn("JQuery:Select", "select");
        }

        public JQuery Select(JSFunction callback)
        {
            return CallPlugIn("JQuery:Select", "select", callback);
        }

        public JQuery Select(JQuery jquery)
        {
            return Select(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Submit()
        {
            return CallPlugIn("JQuery:Submit", "submit");
        }

        public JQuery Submit(JSFunction callback)
        {
            return CallPlugIn("JQuery:Submit", "submit", callback);
        }

        public JQuery Submit(JQuery jquery)
        {
            return Submit(Extensions.ToFunctionExpression(jquery));
        }

        public JQuery Unload(JSFunction callback)
        {
            return CallPlugIn("JQuery:Unload", "unload", callback);
        }

        public JQuery Unload(JQuery jquery)
        {
            return CallPlugIn("JQuery:Unload", "unload", Extensions.ToFunctionExpression(jquery));
        }

        #endregion

        #region Effects

        public JQuery Animate(object parameters, object options)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, options);
        }

        public JQuery Animate(object parameters, int duration)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, duration);
        }

        public JQuery Animate(object parameters, string duration)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, duration);
        }

        public JQuery Animate(object parameters, int duration, string easing)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, duration, easing);
        }

        public JQuery Animate(object parameters, string duration, string easing)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, duration, easing);
        }

        public JQuery Animate(object parameters, int duration, string easing, string callback)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, duration, easing, callback);
        }

        public JQuery Animate(object parameters, string duration, string easing, string callback)
        {
            return CallPlugIn("JQuery:Animate", "animate", parameters, duration, easing, callback);
        }

        public JQuery Animate(object parameters, int duration, string easing, JQuery callback)
        {
            return Animate(parameters, duration, easing, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery Animate(object parameters, string duration, string easing, JQuery callback)
        {
            return Animate(parameters, duration, easing, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery Delay(int duration)
        {
            return CallPlugIn("JQuery:Delay", "delay", duration);
        }

        #region FadeIn

        public JQuery FadeIn(int duration)
        {
            return CallPlugIn("JQuery:FadeIn", "fadeIn", duration);
        }

        public JQuery FadeIn(string speed)
        {
            return CallPlugIn("JQuery:FadeIn", "fadeIn", speed);
        }

        public JQuery FadeIn(int duration, string callback)
        {
            return FadeIn(duration, new JSFunctionString(callback));
        }

        public JQuery FadeIn(string speed, string callback)
        {
            return CallPlugIn("JQuery:FadeIn", "fadeIn",
                new List<object>() { { speed }, { callback } });
        }

        public JQuery FadeIn(string speed, JSObject callback)
        {
            return CallPlugIn("JQuery:FadeIn", "fadeIn",
                new List<object>() { { speed }, { callback } });
        }

        public JQuery FadeIn(int duration, JSObject callback)
        {
            return CallPlugIn("JQuery:FadeIn", "fadeIn",
                new List<object>() { { duration }, { callback } });
        }

        public JQuery FadeIn(int duration, JQuery callback)
        {
            return FadeIn(duration, callback.ToFunctionExpression());
        }

        public JQuery FadeIn(string speed, JQuery callback)
        {
            return FadeIn(speed, callback.ToFunctionExpression());
        }

        #endregion

        #region FadeOut

        public JQuery FadeOut(int duration)
        {
            return CallPlugIn("JQuery:FadeOut", "fadeOut", duration);
        }

        public JQuery FadeOut(string speed)
        {
            return CallPlugIn("JQuery:FadeOut", "fadeOut", speed);
        }

        public JQuery FadeOut(int duration, string callback)
        {
            return FadeOut(duration, new JSFunctionString(callback));

        }

        public JQuery FadeOut(string speed, string callback)
        {
            return FadeOut(speed, new JSFunctionString(callback));
        }

        public JQuery FadeOut(int duration, JSObject callback)
        {
            return CallPlugIn("JQuery:FadeOut", "fadeOut",
                new List<object>() { { duration }, { callback } });
        }

        public JQuery FadeOut(string speed, JSObject callback)
        {
            return CallPlugIn("JQuery:FadeOut", "fadeOut",
                new List<object>() { { speed }, { callback } });
        }

        public JQuery FadeOut(int duration, JQuery callback)
        {
            return FadeOut(duration, callback.ToFunctionExpression());
        }

        public JQuery FadeOut(string speed, JQuery callback)
        {
            return FadeOut(speed, callback.ToFunctionExpression());
        }

        #endregion

        #region FadeTo

        public JQuery FadeTo(int duration, double opacity)
        {
            return CallPlugIn("JQuery:FadeTo", "fadeTo",
                new List<object>() { { duration }, { opacity } });
        }

        public JQuery FadeTo(string speed, double opacity)
        {
            return CallPlugIn("JQuery:FadeTo", "fadeTo",
                new List<object>() { { speed }, { opacity } });
        }

        public JQuery FadeTo(int duration, double opacity, string callback)
        {
            return FadeTo(duration, opacity, new JSFunctionString(callback));
        }

        public JQuery FadeTo(string speed, double opacity, string callback)
        {
            return FadeTo(speed, opacity, new JSFunctionString(callback));
        }

        public JQuery FadeTo(int duration, double opacity, JSObject callback)
        {
            return CallPlugIn("JQuery:FadeTo", "fadeTo",
                new List<object>() { { duration }, { opacity }, { callback } });
        }

        public JQuery FadeTo(string speed, double opacity, JSObject callback)
        {
            return CallPlugIn("JQuery:FadeTo", "fadeTo",
                new List<object>() { { speed }, { opacity }, { callback } });
        }

        public JQuery FadeTo(int duration, double opacity, JQuery callback)
        {
            return FadeTo(duration, opacity, callback.ToFunctionExpression());
        }

        public JQuery FadeTo(string speed, double opacity, JQuery callback)
        {
            return FadeTo(speed, opacity, callback.ToFunctionExpression());
        }

        #endregion

        public JQuery Hide()
        {
            return CallPlugIn("JQuery:Hide", "hide");
        }

        public JQuery Hide(int duration)
        {
            return CallPlugIn("JQuery:Hide", "hide", duration);
        }

        public JQuery Hide(string speed)
        {
            return CallPlugIn("JQuery:Hide", "hide", speed);
        }

        public JQuery Hide(int duration, string callback)
        {
            return CallPlugIn("JQuery:Hide", "hide",
                new Dictionary<string, object>() { { "Val", duration }, { "Callback", callback } });
        }

        public JQuery Hide(string speed, string callback)
        {
            return CallPlugIn("JQuery:Hide", "hide",
                new Dictionary<string, object>() { { "Val", speed }, { "Callback", callback } });
        }


        public JQuery Hide(int duration, JQuery callback)
        {
            return Hide(duration, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery Hide(string speed, JQuery callback)
        {
            return Hide(speed, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery Show()
        {
            return CallPlugIn("JQuery:Show", "show");
        }

        public JQuery Show(int duration)
        {
            return CallPlugIn("JQuery:Show", "show", duration);
        }

        public JQuery Show(string speed)
        {
            return CallPlugIn("JQuery:Show", "show", speed);
        }

        public JQuery Show(int duration, string callback)
        {
            return CallPlugIn("JQuery:Show", "show",
                new Dictionary<string, object>() { { "Val", duration }, { "Callback", callback } });
        }

        public JQuery Show(string speed, string callback)
        {
            return CallPlugIn("JQuery:Show", "show",
                new Dictionary<string, object>() { { "Val", speed }, { "Callback", callback } });
        }

        public JQuery SlideDown(int duration)
        {
            return CallPlugIn("JQuery:SlideDown", "slideDown", duration);
        }

        public JQuery SlideDown(string speed)
        {
            return CallPlugIn("JQuery:SlideDown", "slideDown", speed);
        }

        public JQuery SlideDown(int duration, string callback)
        {
            return CallPlugIn("JQuery:SlideDown", "slideDown",
                new Dictionary<string, object>() { { "Val", duration }, { "Callback", callback } });
        }

        public JQuery SlideDown(string speed, string callback)
        {
            return CallPlugIn("JQuery:SlideDown", "slideDown",
                new Dictionary<string, object>() { { "Val", speed }, { "Callback", callback } });
        }


        public JQuery SlideDown(int duration, JQuery callback)
        {
            return SlideDown(duration, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery SlideDown(string speed, JQuery callback)
        {
            return SlideDown(speed, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        #region SlideToggle

        public JQuery SlideToggle(int duration)
        {
            return CallPlugIn("JQuery:SlideToggle", "slideToggle", duration);
        }

        public JQuery SlideToggle(string speed)
        {
            return CallPlugIn("JQuery:SlideToggle", "slideToggle", speed);
        }

        public JQuery SlideToggle(int duration, string callback)
        {
            return SlideToggle(duration, new JSFunctionString(callback));
        }

        public JQuery SlideToggle(string speed, string callback)
        {
            return SlideToggle(speed, new JSFunctionString(callback));
        }

        public JQuery SlideToggle(int duration, JSObject callback)
        {
            return CallPlugIn("JQuery:SlideToggle", "slideToggle",
                new List<object>() { { duration }, { callback } });
        }

        public JQuery SlideToggle(string speed, JSObject callback)
        {
            return CallPlugIn("JQuery:SlideToggle", "slideToggle",
                new List<object>() { { speed }, { callback } });
        }

        public JQuery SlideToggle(int duration, JQuery callback)
        {
            return SlideToggle(duration, callback.ToFunctionExpression());
        }

        public JQuery SlideToggle(string speed, JQuery callback)
        {
            return SlideToggle(speed, callback.ToFunctionExpression());
        }

        #endregion

        public JQuery SlideUp(int duration)
        {
            return CallPlugIn("JQuery:SlideUp", "slideUp", duration);
        }

        public JQuery SlideUp(string speed)
        {
            return CallPlugIn("JQuery:SlideUp", "slideUp", speed);
        }

        public JQuery SlideUp(int duration, string callback)
        {
            return CallPlugIn("JQuery:SlideUp", "slideUp",
                new Dictionary<string, object>() { { "Val", duration }, { "Callback", callback } });
        }

        public JQuery SlideUp(string speed, string callback)
        {
            return CallPlugIn("JQuery:SlideUp", "slideUp",
                new Dictionary<string, object>() { { "Val", speed }, { "Callback", callback } });
        }


        public JQuery SlideUp(int duration, JQuery callback)
        {
            return SlideUp(duration, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery SlideUp(string speed, JQuery callback)
        {
            return SlideUp(speed, Extensions.ToFunctionExpression(callback.ToScript()));
        }

        public JQuery Stop()
        {
            return CallPlugIn("JQuery:Stop", "stop");
        }

        public JQuery Toggle()
        {
            return CallPlugIn("JQuery:Toggle", "toggle");
        }

        public JQuery CallPlugIn(string operationType, string plugin, Dictionary<string, object> parameters)
        {
            AddOperation(operationType, parameters, "." + plugin);
            return this;
        }

        public JQuery CallPlugIn(string plugin, params object[] parameters)
        {
            return CallPlugIn("JQuery:PlugIn:" + plugin, plugin, parameters);
        }

        public JQuery CallPlugIn(string operationType, string plugin, params object[] parameters)
        {
            AddOperation(operationType, parameters, "." + plugin);
            return this;
        }

        public JQuery CallPlugIn(string operationType, string plugin, object singleParameterValue)
        {
            AddOperation(operationType, singleParameterValue, "." + plugin);
            return this;
        }

        public JQuery CallPlugIn(string plugin, Dictionary<string, object> parameters)
        {
            return CallPlugIn("JQuery:PlugIn:" + plugin, plugin, parameters);
        }

        public JQuery CallPlugIn(string plugin, object singleParameterValue)
        {
            return CallPlugIn("JQuery:PlugIn:" + plugin, plugin, singleParameterValue);
        }

        #endregion

        #region Attributes
        // See http://docs.jquery.com/Attributes

        public JQuery Attr(string name, string value)
        {
            return CallPlugIn("JQuery:Attr", "attr",
                new Dictionary<string, object>() { { "Name", name }, { "Value", value } });
        }

        public JQuery RemoveAttr(string name)
        {
            return CallPlugIn("JQuery:RemoveAttr", "removeAttr", name);
        }

        public JQuery AddClass(string name)
        {
            return CallPlugIn("JQuery:AddClass", "addClass", name);
        }

        //TODO: HasClass(string class)

        public JQuery RemoveClass()
        {
            return CallPlugIn("JQuery:RemoveClass", "removeClass");
        }

        public JQuery RemoveClass(string name)
        {
            return CallPlugIn("JQuery:RemoveClass", "removeClass", name);
        }

        public JQuery ToggleClass(string name)
        {
            return CallPlugIn("JQuery:ToggleClass", "toggleClass", name);
        }

        public JQuery Html()
        {
            return CallPlugIn("JQuery:Html", "html");
        }

        public JQuery Html(string val)
        {
            return CallPlugIn("JQuery:Html", "html", val);
        }

        public JQuery Html(DomResult val)
        {
            return CallPlugIn("JQuery:Html", "html", val);
        }

        //TODO: string Text()
        /*
        public string Text()	// hmm, will this work? :-~
        {
            return CallPlugIn("JQuery:Text", String.Empty,
                ".text()");
            return Owner.ToScript();;
        }
        */

        public JQuery Text(string val)
        {
            return CallPlugIn("JQuery:Text", "text", val);
        }

        public DomResult Val()
        {
            return CallPlugIn("JQuery:Val", "val");
        }

        public JQuery Val(string value)
        {
            return CallPlugIn("JQuery:Val", "val", value);
        }

        #endregion

        #region Traversing
        // See http://docs.jquery.com/Traversing

        public JQuery Filter(string expr)
        {
            return CallPlugIn("JQuery:Filter", "filter", expr);
        }

        public JQuery FilterFunction(string callback)
        {
            return CallPlugIn("JQuery:FilterFunction", "filter", callback);
        }

        //TODO: Is()

        public JQuery Map(string callback)
        {
            return CallPlugIn("JQuery:Map", "map", callback);
        }

        public JQuery Not(string expr)
        {
            return CallPlugIn("JQuery:Not", "not", expr);
        }

        public JQuery Slice(int start, int end)
        {
            return CallPlugIn("JQuery:Slice", "slice",
                new Dictionary<string, object>() { { "Start", start }, { "End", end } });
        }

        public JQuery Add(string expr)
        {
            return CallPlugIn("JQuery:Add", "add", expr);
        }

        public JQuery Children()
        {
            return CallPlugIn("JQuery:Children", "children");
        }

        public JQuery Children(string expr)
        {
            return CallPlugIn("JQuery:Children", "children", expr);
        }

        //TODO: Closest

        public JQuery Contents()
        {
            return CallPlugIn("JQuery:Contents", "contents");
        }

        public JQuery Find(string expr)
        {
            return CallPlugIn("JQuery:Find", "find", expr);
        }

        public JQuery Next()
        {
            return CallPlugIn("JQuery:Next", "next");
        }

        public JQuery Next(string expr)
        {
            return CallPlugIn("JQuery:Next", "next", expr);
        }

        public JQuery NextAll()
        {
            return CallPlugIn("JQuery:NextAll", "nextAll");
        }

        public JQuery NextAll(string expr)
        {
            return CallPlugIn("JQuery:NextAll", "nextAll", expr);
        }

        public JQuery Parent()
        {
            return CallPlugIn("JQuery:Parent", "parent");
        }

        public JQuery Parent(string expr)
        {
            return CallPlugIn("JQuery:Parent", "parent", expr);
        }

        public JQuery Parents()
        {
            return CallPlugIn("JQuery:Parents", "parents");
        }

        public JQuery Parents(string expr)
        {
            return CallPlugIn("JQuery:Parents", "parents", expr);
        }

        public JQuery Prev()
        {
            return CallPlugIn("JQuery:Prev", "prev");
        }

        public JQuery Prev(string expr)
        {
            return CallPlugIn("JQuery:Prev", "prev", expr);
        }

        public JQuery PrevAll()
        {
            return CallPlugIn("JQuery:PrevAll", "prevAll");
        }

        public JQuery PrevAll(string expr)
        {
            return CallPlugIn("JQuery:PrevAll", "prevAll", expr);
        }

        public JQuery Siblings()
        {
            return CallPlugIn("JQuery:Siblings", "siblings");
        }

        public JQuery Siblings(string expr)
        {
            return CallPlugIn("JQuery:Siblings", "siblings", expr);
        }

        public JQuery AndSelf()
        {
            return CallPlugIn("JQuery:AndSelf", "andSelf");
        }

        public JQuery End()
        {
            return CallPlugIn("JQuery:End", "end");
        }

        #endregion

        #region Manipulation
        // See http://docs.jquery.com/Manipulation

        // Html and Text are under Attributes

        public JQuery Append(string expr)
        {
            return CallPlugIn("JQuery:Append", "append", expr);
        }

        public JQuery Append(JQuery jquery)
        {
            return CallPlugIn("JQuery:Append", "append", jquery);
        }

        public JQuery AppendTo(string expr)
        {
            return CallPlugIn("JQuery:AppendTo", "appendTo", expr);
        }

        public JQuery Prepend(string expr)
        {
            return CallPlugIn("JQuery:Prepend", "prepend", expr);
        }

        public JQuery Prepend(JQuery jquery)
        {
            return CallPlugIn("JQuery:Prepend", "prepend", jquery);
        }

        public JQuery PrependTo(string expr)
        {
            return CallPlugIn("JQuery:PrependTo", "prependTo", expr);
        }

        public JQuery After(string expr)
        {
            return CallPlugIn("JQuery:After", "after", expr);
        }

        public JQuery After(JQuery jquery)
        {
            return CallPlugIn("JQuery:After", "after", jquery);
        }

        public JQuery Before(string expr)
        {
            return CallPlugIn("JQuery:Before", "before", expr);
        }

        public JQuery Before(JQuery jquery)
        {
            return CallPlugIn("JQuery:Before", "before", jquery);
        }

        public JQuery InsertAfter(string expr)
        {
            return CallPlugIn("JQuery:InsertAfter", "insertAfter", expr);
        }

        public JQuery InsertBefore(string expr)
        {
            return CallPlugIn("JQuery:InsertBefore", "insertBefore", expr);
        }

        public JQuery Wrap(string html)
        {
            return CallPlugIn("JQuery:Wrap", "wrap", html);
        }

        public JQuery WrapAll(string html)
        {
            return CallPlugIn("JQuery:WrapAll", "wrapAll", html);
        }

        public JQuery WrapInner(string html)
        {
            return CallPlugIn("JQuery:WrapInner", "wrapInner", html);
        }

        public JQuery ReplaceAll(string expr)
        {
            return CallPlugIn("JQuery:ReplaceAll", "replaceAll", expr);
        }

        public JQuery ReplaceAll(JQuery jquery)
        {
            return CallPlugIn("JQuery:ReplaceAll", "replaceAll", jquery);
        }

        public JQuery ReplaceWith(string expr)
        {
            return CallPlugIn("JQuery:ReplaceWith", "replaceWith", expr);
        }

        public JQuery ReplaceWith(JQuery jquery)
        {
            return CallPlugIn("JQuery:ReplaceWith", "replaceWith", jquery);
        }

        public JQuery Empty()
        {
            return CallPlugIn("JQuery:Empty", "empty");
        }

        public JQuery Remove()
        {
            return CallPlugIn("JQuery:Remove", "remove");
        }

        public JQuery Remove(string expr)
        {
            return CallPlugIn("JQuery:Remove", "remove", expr);
        }

        public JQuery Clone()
        {
            return Clone(false);
        }

        public JQuery Clone(bool cloneEvents)
        {
            return CallPlugIn("JQuery:Clone", "clone", cloneEvents);
        }

        public JSObject Value
        {
            get { return CallPlugIn("JQuery:Value", "val", (object)null); }
            set { CallPlugIn("JQuery:Value", "val", value); }
        }

        #endregion

        #region CSS

        public JQuery Css(object properties)
        {
            return CallPlugIn("JQuery:Css", "css", new JavaScriptSerializer().Serialize(properties));
        }

        public JQuery Css(Dictionary<string, object> properties)
        {
            return Css(properties);
        }

        public JQuery Css(string name, string value)
        {
            return CallPlugIn("JQuery:Css", "css",
                new Dictionary<string, object>() { { "Name", name }, { "Value", value } });
        }

        //TODO: ScrollTop and ScrollLeft


        public JQuery Height(string val)
        {
            return CallPlugIn("JQuery:Height", "height", val);
        }

        public JQuery Height(int val)
        {
            return CallPlugIn("JQuery:Height", "height", val);
        }

        public JQuery Width(string val)
        {
            return CallPlugIn("JQuery:Width", "width", val);
        }

        public JQuery Width(int val)
        {
            return CallPlugIn("JQuery:Width", "width", val);
        }

        #endregion

        #region Ajax

        public JQuery Load(string url)
        {
            return CallPlugIn("JQuery:Load", "load", url);
        }

        public JQuery Load(string url, Dictionary<string, object> data)
        {
            return Load(url, data);
        }

        public JQuery Load(string url, object data)
        {
            return CallPlugIn("JQuery:Load", "load",
                new Dictionary<string, object>() { { "Url", url }, { "Data", serializer.Serialize(data) } });
        }

        public JQuery Load(string url, Dictionary<string, object> data, string callback)
        {
            return Load(url, (object)data, callback);
        }

        public JQuery Load(string url, object data, string callback)
        {
            return CallPlugIn("JQuery:Load", "load",
                new Dictionary<string, object>() { { "Url", url }, { "Data", serializer.Serialize(data) }, { "Callback", callback } });
        }
        #endregion

        #region Utilities
        // See http://docs.jquery.com/Utilities

        #endregion

        public JQuery DisableSelection()
        {
            return CallPlugIn("JQuery:DisableSelection", "disableSelection");
        }
        public JQuery EnableSelection()
        {
            return CallPlugIn("JQuery:EnableSelection", "enableSelection");
        }

        public JSFunction ToFunctionExpression()
        {
            return Extensions.ToFunctionExpression(this);
        }

        public static AjaxQuery Ajax()
        {
            return new AjaxQuery();
        }

        public static JQuery Tmpl(string template, object data)
        {
            var jq = new JQuery((JSObject)null);
            jq.AddOperation("tmpl", new object[] { template, data }, "$.tmpl");
            return jq;
        }

        public override string ToString()
        {
            return ToScript();
        }
    }

    //public static class JQueryExtensions
    //{
    //    public static JQuery JQuery(DomResult domResult, string query)
    //    {
    //        return new JQuery(domResult, query);
    //    }

    //    public static JQuery JQueryWith(DomResult domResult, string parameterName)
    //    {
    //        return new JQuery(domResult, parameterName, true);
    //    }
    //}

    public enum ToggleEffect
    {
        Blind,
        Clip,
        Drop,
        Explode,
        Fold,
        Puff,
        Slide,
        Scale,
        Size,
        Pulsate
    }
}